package cn.tedu.redissondemo.controller;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
public class TestRedisController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private Redisson redisson;
    @Value("${server.port}")
    private String port;



    //JVM级别的锁
    @RequestMapping("/test")
    @ResponseBody
    private String TestRedis(){

        synchronized (this){
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
                redisTemplate.opsForValue().set("stock",realStock+"");
                System.out.println("扣减成功,剩余库存"+realStock+"个"+port);
                return "扣减成功,剩余库存"+realStock+"个"+port;
            }else{
                return "扣减失败,库存不足"+port;
            }
        }
    }

    //redis例用setNx稍微优化,执行完业务后删除key
    @RequestMapping("/test1")
    @ResponseBody
    private String TestRedis1(){
        String lockKey = "lockKey";
        String msg = "";
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)存在则不
        if(!result){
            System.out.println("系统繁忙请稍后重试!!");
            return "系统繁忙请稍后重试!!";
        }

        Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if(stock>0){
            int realStock = stock -1 ;
            redisTemplate.opsForValue().set("stock",realStock+"");
            System.out.println("扣减成功,剩余库存"+realStock+"个"+port);
            msg = "扣减成功,剩余库存"+realStock+"个"+port;
        }else{
            msg =  "扣减失败,库存不足"+port;
        }
        redisTemplate.delete(lockKey);
        return msg;
    }
    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题
    @RequestMapping("/test2")
    @ResponseBody
    private String TestRedis2(){
        String lockKey = "lockKey";
        String msg = "";
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)
            if(!result){
                return "系统繁忙请稍后重试!!";
            }

            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
                redisTemplate.opsForValue().set("stock",realStock+"");
                System.out.println("扣减成功,剩余库存"+realStock+"个"+port);
                msg = "扣减成功,剩余库存"+realStock+"个"+port;
            }else{
                msg =  "扣减失败,库存不足"+port;
            }
        }finally {
            redisTemplate.delete(lockKey);
        }
        return msg;
    }
    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题,设置超时时间,解决Jvmdown机问题
    @RequestMapping("/test3")
    @ResponseBody
    private String TestRedis3(){
        String lockKey = "lockKey";
        String msg = "";
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)
            redisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            if(!result){
                return "系统繁忙请稍后重试!!";
            }

            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
                redisTemplate.opsForValue().set("stock",realStock+"");
                System.out.println("扣减成功,剩余库存"+realStock+"个"+port);
                msg = "扣减成功,剩余库存"+realStock+"个"+port;
            }else{
                msg =  "扣减失败,库存不足"+port;
            }
        }finally {
            redisTemplate.delete(lockKey);
        }
        return msg;
    }

    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题,设置超时时间,解决Jvmdown机问题
    //设置超时时间和不可以重复key为原子性操作
    @RequestMapping("/test4")
    @ResponseBody
    private String TestRedis4(){
        String lockKey = "lockKey";
        String msg = "";

        try {
            //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)
            //redisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx",10,TimeUnit.SECONDS);//jedis.setnxex(key,value)
            System.out.println(result);

            if(!result){
                //System.out.println("系统繁忙请稍后重试!!");
                return "系统繁忙请稍后重试!!";
            }
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
                redisTemplate.opsForValue().set("stock",realStock+"");
                msg = "扣减成功,剩余库存"+realStock+"个"+port;

            }else{
                msg =  "扣减失败,库存不足"+port;
            }
        }finally {
            redisTemplate.delete(lockKey);
        }
        System.out.println(msg);
        return msg;
    }

    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题,设置超时时间,解决Jvmdown机问题
    //设置超时时间和不可以重复key为原子性操作
    // 不能删除别人加的锁,所以添加了valueUUID 所以增加UUID.或者用户ID  从此处开始并发200 0 4 参数已经可以绝大部分安全问题了
    @RequestMapping("/test5")
    @ResponseBody
    private String TestRedis5(){
        String lockKey = "lockKey";
        String msg = "";
        String uuid = UUID.randomUUID().toString();
        try {
            //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)
            //redisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid,10,TimeUnit.SECONDS);//jedis.setnxex(key,value)
            

            if(!result){
                return "系统繁忙请稍后重试!!";
            }

            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
                redisTemplate.opsForValue().set("stock",realStock+"");
                msg = "扣减成功,剩余库存"+realStock+"个"+port;
            }else{
                msg =  "扣减失败,库存不足"+port;
            }
        }finally {
            if(uuid.equals(redisTemplate.opsForValue().get(lockKey))){
                redisTemplate.delete(lockKey);
            }
        }
        System.out.println(msg+Thread.currentThread().getName());
        return msg;
    }

    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题,设置超时时间,解决Jvmdown机问题
    //设置超时时间和不可以重复key为原子性操作
    // 不能删除别人加的锁,所以增加UUID.或者用户ID
    //关于Key过期时间设置多少的问题,此前是10s,需要考虑超时时间超过10S后,key失效但业务没完成的情况,锁超时续命,解决方案redisson
    @RequestMapping("/test6")
    @ResponseBody
    private String TestRedis6(){
        String lockKey = "lockKey";
        String msg = "";
        //String uuid = UUID.randomUUID().toString();
        RLock redissonLock = redisson.getLock(lockKey);//获取锁对象
        try {
            //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "jx");//jedis.setnx(key,value)
            //redisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid,10,TimeUnit.SECONDS);//jedis.setnxex(key,value)
            //if(!result){
            //    return "系统繁忙请稍后重试!!";
            //}

            //加锁
            redissonLock.lock(); //加锁
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock -1 ;
//                Thread.sleep(10);
                redisTemplate.opsForValue().set("stock",realStock+"");
                msg = "扣减成功,剩余库存"+realStock+"个"+port;
            }else{
                msg =  "扣减失败,库存不足"+port;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            redissonLock.unlock(); //释放锁
            //if(uuid.equals(redisTemplate.opsForValue().get(lockKey))){
            //    redisTemplate.delete(lockKey);
            //}
        }
        System.out.println(msg+port);
        return msg;
    }
    //redis例用setNx稍微优化,执行完业务后删除key,解决中途异常不释放锁,造成死锁的问题,设置超时时间,解决Jvmdown机问题
    //设置超时时间和不可以重复key为原子性操作
    // 不能删除别人加的锁,所以增加UUID.或者用户ID
    //关于Key过期时间设置多少的问题,此前是10s,需要考虑超时时间超过10S后,key失效但业务没完成的情况,锁超时续命,解决方案redisson
    //自己尝试 数据分段提升性能
    @RequestMapping("/test7")
    @ResponseBody
    private String TestRedis7(){
        int i =(int) (Math.random()*10);
        String lockKey = "lockKey"+i;
        String msg = "";
        RLock redissonLock = redisson.getLock(lockKey);
        try {
            redissonLock.lock();
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock" + i));
            if(stock>0){
                int realStock = stock -1 ;
                Thread.sleep(10);
                redisTemplate.opsForValue().set("stock"+i,realStock+"");
                msg="扣减成功,stock"+i+",剩余库存: "+realStock+"个"+port;
            }else{
                Thread.sleep(10);
                msg = "扣减失败,库存不足" + port;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redissonLock.unlock();
        }

        System.out.println(msg);
        return msg;
    }


    @RequestMapping("/test15")
    @ResponseBody
    private String TestRedis15(){
        for (int i = 0 ;i<10 ;i++){
            redisTemplate.opsForValue().set("stock"+i,"80");
        }
       return "ok";
    }

}
